package reflect

import (
	"jvmgo/instructions/base"
	"jvmgo/native"
	"jvmgo/rtda"
	"jvmgo/rtda/heap"
)

func init() {
	native.Register("sun/reflect/NativeConstructorAccessorImpl", "newInstance0", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;", newInstance0)
}

func newInstance0(frame *rtda.Frame) {
	vars := frame.LocalVars()
	constructorObj := vars.GetRef(0)
	argArrObj := vars.GetRef(1)
	goConstructor := getGoConstructor(constructorObj)
	goClass := goConstructor.Class()
	if !goClass.InitStarted() {
		frame.RevertNextPC()
		base.InitClass(frame.Thread(), goClass)
		return
	}
	obj := goClass.NewObject()
	stack := frame.OperandStack()
	stack.PushRef(obj)

	ops := convertArgs(obj, argArrObj, goConstructor)
	shimFrame := rtda.NewShimFrame(frame.Thread(), ops)
	frame.Thread().PushFrame(shimFrame)
	base.InvokeMethod(shimFrame, goConstructor)
}

func convertArgs(this *heap.Object, argArr *heap.Object, method *heap.Method) *rtda.OperandStack {
	if method.ArgSlotCount() == 0 {
		return nil
	}
	ops := rtda.NewOperandStack(method.ArgSlotCount())
	if !method.IsStatic() {
		ops.PushRef(this)
	}
	if method.ArgSlotCount() == 1 && !method.IsStatic() {
		return ops
	}
	return ops
}

func getGoConstructor(obj *heap.Object) *heap.Method {
	return _getGoMethod(obj, true)
}

func _getGoMethod(methodObj *heap.Object, isConstructor bool) *heap.Method {
	extra := methodObj.Extra()
	if extra != nil {
		return extra.(*heap.Method)
	}
	if isConstructor {
		root := methodObj.GetRefVar("root", "Ljava/lang/reflect/Constructor;")
		return root.Extra().(*heap.Method)
	}
	root := methodObj.GetRefVar("root", "Ljava/lang/reflect/Method;")
	return root.Extra().(*heap.Method)
}
